﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.IO;
using SevenZip;
using System.Threading;
using ComicSharp.Helpers;

namespace ComicSharp
{
    public partial class frmViewer : Form
    {
        #region Properties and Delegates

        delegate void SetNoArgsDelegate();  // 7-Zip Unarchive Delegate
        private int position = 0;           // Current page position
        private string[] pages;             // File path of each page

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="frmViewer"/> class.
        /// </summary>
        public frmViewer()
        {
            InitializeComponent();
        }

        #endregion

        #region Events

        /// <summary>
        /// Handles the Load event of the frmViewer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void frmViewer_Load(object sender, EventArgs e)
        {
            // Display the open file dialog
            ShowOpenComicDialog();
        }

        /// <summary>
        /// Handles the FormClosing event of the frmViewer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.FormClosingEventArgs"/> instance containing the event data.</param>
        private void frmViewer_FormClosing(object sender, FormClosingEventArgs e)
        {
            // Clean up resources
            pbPageLeft.Dispose();
        }

        /// <summary>
        /// Handles the MouseClick event of the pbPage control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
        private void pbPage_MouseClick(object sender, MouseEventArgs e)
        {
            // Perform action depending on mouse button click
            switch (e.Button)
            {
                case MouseButtons.Left:     // Previous Page

                    if (ComicSharp.Default.IsPortraitMode)
                    {
                        position-= 2;
                    }
                    else
                    {
                        position--;
                    }

                    LoadImage();
                    break;

                case MouseButtons.Middle:   // Opens a new comic book
                    ResetViewer();
                    ShowOpenComicDialog();
                    break;

                case MouseButtons.Right:    // Next Page

                    if (ComicSharp.Default.IsPortraitMode)
                    {
                        position+= 2;
                    }
                    else
                    {
                        position++;
                    }

                    LoadImage();
                    break;

                default:
                    break;
            }
        }

        /// <summary>
        /// Handles the MouseClick event of the pbPageRight control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
        private void pbPageRight_MouseClick(object sender, MouseEventArgs e)
        {
            // Perform action depending on mouse button click
            switch (e.Button)
            {
                case MouseButtons.Left:     // Previous Page

                    if (ComicSharp.Default.IsPortraitMode)
                    {
                        position -= 2;
                    }
                    else
                    {
                        position--;
                    }

                    LoadImage();
                    break;

                case MouseButtons.Middle:   // Opens a new comic book
                    ResetViewer();
                    ShowOpenComicDialog();
                    break;

                case MouseButtons.Right:    // Next Page

                    if (ComicSharp.Default.IsPortraitMode)
                    {
                        position += 2;
                    }
                    else
                    {
                        position++;
                    }

                    LoadImage();
                    break;

                default:
                    break;
            }
        }

        /// <summary>
        /// Processes a command key.
        /// </summary>
        /// <param name="msg">A <see cref="T:System.Windows.Forms.Message"/>, passed by reference, that represents the Win32 message to process.</param>
        /// <param name="keyData">One of the <see cref="T:System.Windows.Forms.Keys"/> values that represents the key to process.</param>
        /// <returns>
        /// true if the keystroke was processed and consumed by the control; otherwise, false to allow further processing.
        /// </returns>
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            // CTRL+S to open the settings form
            if (keyData == (Keys.Control | Keys.S))
            {
                frmSettings settings = new frmSettings();
                settings.ShowDialog();
                return true;
            }

            // CTRL+O to open the open file dialog
            if (keyData == (Keys.Control | Keys.O))
            {
                ShowOpenComicDialog();
                return true;
            }

            return base.ProcessCmdKey(ref msg, keyData);
        }

        #endregion

        #region Methods

        /// <summary>
        /// Shows the open comic dialog.
        /// </summary>
        private void ShowOpenComicDialog()
        {
            // Setup the open file dialog
            ofdOpenComic.Filter = "Comic Files (*.cbr;*.cbz)|*.cbr;*.cbz|All files (*.*)|*.*";
            ofdOpenComic.Title = "Select Comic File";
            ofdOpenComic.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

            // Prompt user and wait for OK response
            if (ofdOpenComic.ShowDialog() == DialogResult.OK)
            {
                // Genreate a temp path and extract comic archive images
                FileHelper.GenerateRandomTempFolder();
                ExtractComicPages(ofdOpenComic.FileName, Program.tempPath);

                // Get a file path listing of all the comic book pages
                pages = FileHelper.GetPages(Program.tempPath);

                // If pages were found, load the first image
                if (pages.Length > 0)
                {
                    LoadImage();
                }
            }
        }

        /// <summary>
        /// Extracts the comic pages.
        /// </summary>
        /// <param name="comicPath">The comic path.</param>
        /// <param name="extractToPath">The extract to path.</param>
        private void ExtractComicPages(string comicPath, string extractToPath)
        {
            // Declare variables
            string fileName = string.Empty;
            string directory = string.Empty;

            // Get the location of the 7-zip DLL
            SevenZipExtractor.SetLibraryPath(FileHelper.GetSevenZipLibraryPath());

            try
            {
                // Extract the comic book archive to the temp location using 7-zip
                this.Invoke(new SetNoArgsDelegate(() =>
                {
                    fileName = comicPath;
                    directory = extractToPath;
                }));

                using (SevenZipExtractor extr = new SevenZipExtractor(fileName))
                {
                    extr.ExtractArchive(directory);
                }
            }
            catch (Exception ex)
            {
                // Display error and rethrow
                MessageBox.Show("ComicSharp Error",
                                    "An error occured while reading the comic book.",
                                    MessageBoxButtons.OK,
                                    MessageBoxIcon.Error);
                throw ex;
            }
        }

        /// <summary>
        /// Loads the image.
        /// </summary>
        private void LoadImage()
        {
            // Declare variables
            ImageHelper.RotateDegrees degrees;

            // Format depending on mode
            if (ComicSharp.Default.IsPortraitMode)
            {
                degrees = ImageHelper.RotateDegrees.Zero;
                pbPageRight.Visible = true;

                // Set up picture box dimensions
                pbPageLeft.Height = this.Height - 5;
                pbPageLeft.Width = (this.Width / 2) - 5;
                pbPageLeft.SizeMode = PictureBoxSizeMode.AutoSize;
            }
            else
            {
                // Rotate 90 deg clockwise
                degrees = ImageHelper.RotateDegrees.Ninety;
                pbPageRight.Visible = false;

                // Set up picture box dimensions
                pbPageLeft.Height = this.Width - 5;
                pbPageLeft.Width = this.Height - 5;
                pbPageLeft.SizeMode = PictureBoxSizeMode.AutoSize;
            }

            try
            {
                // Attempt to scale/rotate/load the bitmap from memory
                pbPageLeft.Image = ImageHelper.RotateAndScaleImage(pages[GetSafePosition(false)],
                                        degrees,
                                        this.Width - 5, 
                                        this.Height - 35);  // Offset the titlebar

                if (pbPageRight.Visible)
                {
                    pbPageRight.Left = pbPageLeft.Width;
                    pbPageRight.Height = this.Height - 5;
                    pbPageRight.Width = (this.Width / 2) - 5;
                    pbPageRight.SizeMode = PictureBoxSizeMode.AutoSize;

                    // Attempt to scale/rotate/load the bitmap from memory
                    pbPageRight.Image = ImageHelper.RotateAndScaleImage(pages[GetSafePosition(true) + 1],
                                            degrees,
                                            this.Width - 5,
                                            this.Height - 35);  // Offset the titlebar
                }
            }
            catch (Exception ex)
            {
                // Display error and rethrow
                MessageBox.Show("ComicSharp Error",
                                    "An error occured while reading the comic book page.",
                                    MessageBoxButtons.OK,
                                    MessageBoxIcon.Error);
                throw ex;
            }
        }

        /// <summary>
        /// Resets the viewer.
        /// </summary>
        private void ResetViewer()
        {
            FileHelper.DeleteTempFolder();
            Program.tempPath = string.Empty;
            pages = null;
            position = 0;
            pbPageLeft.Image = null;
            pbPageRight.Image = null;
        }

        /// <summary>
        /// Gets the safe position.
        /// </summary>
        /// <param name="isRight">if set to <c>true</c> [is right].</param>
        /// <returns>A safe position</returns>
        private int GetSafePosition(Boolean isRight)
        {
            // Check the current image position
            if (position < 0)
            {
                position = 0;
            }

            if (position >= pages.Length)
            {
                position = pages.Length - 1;
            }

            if ((position + 1) >= pages.Length)
            {
                return 0;
            }

            return position;
        }

        #endregion
    }
}
